// ## Utils functions
// Small utilities functions.
//
var _ = require('underscore'),
  logger = require('smart-logger')(module),
  ObjectID = require('mongodb').BSONPure.ObjectID;

var utils = {

  unescapeSmartApiSpecialChars: function(value) {
    if(value) {
      // note: * and ? are preserved as is, it will work as wildcards
      // | and & chars are already escaped because they are special char for smart-api
      // lets unascape them before applying a standard lucene escaping
      value=value.replace('\\&', '&');
      value=value.replace('\\|', '|');
      //var re = /([\\+\-\!\(\):\^\[\]"\{\}~;\|&])/g;
      //value=value.replace(re, '\\$1');
    }
    return value;
  },

  // ### splitQuery
  // splitQuery by & (for and) and | (for or)
  //
  // - **param query {String}** the query string to split
  // - **param split {Object}** the resulting splitted query, that contains the type of the split (i.e.
  // & or | ) and two strings, partOne and partTwo.
  // - **return {boolean}** true if the query string has been splitted, false otherwise
  //
  splitQuery: function(query, split) {
    // TODO: first, split by parenthesis
    var index;

    // first, split the request string by ANDs
    if((index=query.search(/[^\\]&/))!==-1) {
      split.type='&';
      split.partOne=query.substr(0, index+1);
      split.partTwo=query.substr(index+2);
      return true;
    }
    // then try split the request string by ORs
    if((index=query.search(/[^\\]\|/))!==-1) {
      split.type='|';
      split.partOne=query.substr(0, index+1);
      split.partTwo=query.substr(index+2);
      return true;
    }
    return false;
  },

  // ### removeSmartdataAttributes()
  // smartFields is an array of path that must be removed from the document object
  // - **param document {Object}** object to clean
  // - **param smartFields {Array[String]}** each path that must be removed from the document (dotted
  // notation, for exemple: object.subobject.field)
  removeSmartdataAttributes: function(document, smartFields) {
    //console.dir(document);
    var index = 0;
    for(index = 0; index < smartFields.length; index++) {
      // just use the excellent function removeAttribute
      utils.removeAttribute(document, smartFields[index], true);
    }
  },

  // ### setAttribute()
  // Modify the value of an attribute in a (sub)object.
  // Can create all missing object along the modification path.
  //
  // - **param node {Object}** node in which the attribute is added.
  // - **param path {String|Array}** path to the modified attribute. Array of
  // strings, or string with names linked with dots.
  // - **param value {Object}** new value of the modified attribute.
  //
  setAttribute: function (node, path, value) {
    //console.log('setAttribute('+node+','+path+','+value+')');
    if (_.isString(path)) {
      path = path.split('.');
    }
    if (!_.isArray(path)) {
      throw new Error('Cannot add an attribute with an empty path !');
    }
    if (path.length > 1) {
      // Removes the first step
      var step = path.splice(0, 1);
      // creates it if necessary
      if (!(step in node)) {
        node[step] = {};
      }
      // and recurse.
      utils.setAttribute(node[step], path, value);
    } else if (path.length === 1) {
       // finally write value
       //console.log('write value: '+path[0]+': '+value);
      node[path[0]] = value;
    } else {
      throw new Error('Cannot add an attribute with an empty path !');
    }
  },

  // ### removeAttribute()
  // Remove an attibute of an object
  //
  // - **param node {Object}** node from which the attribute is removed.
  // - **param path {String}** path of the attribute to remove, in dot notation, from the current node.
  // - **param removeEmpty {boolean}** whether to remove empty object generated by removing attributes
  //
  removeAttribute: function removeAttribute(node, path, removeEmpty, backPath) {
    var index = 0, pathVar;
    if (_.isString(path)) {
      path = path.split('.');
    }
    if (!_.isArray(path)) {
      throw new Error('Cannot remove an attribute with an empty path !');
    }
    if(_.isArray(node)) {
      for(index = 0; index < node.length; index++) {
        pathVar = path.join('.');
        removeAttribute(node[index], pathVar, removeEmpty, (backPath?backPath:'')+'['+index+']');
      }
      if(removeEmpty) {
        for(index = node.length-1; index >= 0 ; index--) {
          if(_.isEmpty(node[index])) {
            node.splice(index, 1);
          }
        }
      }
      return;
    }
    if (path.length > 1) {
      // Removes the first step
      var step = path.splice(0, 1);
      if (!(step in node)) {
        return;
      }
      // and recurse.
      pathVar = path.join('.');
      removeAttribute(node[step], pathVar, removeEmpty, (backPath?backPath+'.':'')+step);
      if(removeEmpty&&_.isEmpty(node[step])) {
        delete node[step];
      }
    } else if (path.length === 1) {
       delete node[path[0]];
       return;
    } else {
      throw new Error('Cannot remove an attribute with an empty path !');
    }
  },

  // ### getAttribute()
  // Return an attribute value of a (sub)object.
  // Can removes the retrived attributes, and removes all upper empty parents.
  //
  // - **param node {Object}** node in which the attribute is searched.
  // - **param path {String|Array}** path to the retrieved attribute. Array of
  // strings, or string with names linked with dots.
  // - **param remove {Boolean}** true to remove the retrieved attribute.
  // - **param removeEmpty {Boolean}** true to remove all parents of the
  // retrieved attribute that are empty.
  // - **return {Object}** value of the searched attribute.
  //
  getAttribute: function getAttribute(node, path) {
    var step, value, arrValue = [], index, pathVar;
    if(path==='') {
      return node;
    }
    if (_.isString(path)) {
      path = path.split('.');
    }
    if (!_.isArray(path)) {
      throw new Error('Cannot get an attribute with an empty path !');
    }
    if (_.isArray(node)) {
      for(index = 0; index < node.length; index++){
        pathVar = path.join('.');
        value = getAttribute(node[index], pathVar);
        if(value!==undefined) {
          arrValue=arrValue.concat(value);
        }
      }
      if(arrValue.length===0) {
        return;
      }
      return arrValue;
    }
    if (path.length > 1) {
      step = path.splice(0, 1);
      if (step in node) {
        pathVar = path.join('.');
        value = getAttribute(node[step], pathVar);
        return value;
      }
      return;
    } else if (path.length === 1) {
      // value found
      return node[path[0]];
    } else {
      throw new Error('Cannot get an attribute with an empty path !');
    }
  },

  // Validates that what should be an ObjectId *is really* an ObjectID
  // or something that can be used as an ObjectId in Mongoose
  validateObjectId : function validateObjectId (id) {
    return (
      id instanceof ObjectID || (
        typeof id === 'string' &&
            (
              id.length==12  ||  (
                id.length==24 && id.match(/^[a-fA-F0-9]{24}$/)
              )
            )
          )
        )
  }
};

module.exports = utils;

